package com.fc.v2.WX;

import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import org.apache.http.client.HttpClient;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Scanner;

public class WXUtil {
    public static PrivateKey getPrivateKey() {
        String s = readFile("D:\\_Document\\fanmini\\1353229602_20210310_cert\\apiclient_key.pem");
//        System.out.println("===========file:"+s);
        return getPrivateKey(s);
    }


    /**
     * 获取私钥的PrivateKey对象
     * @param key
     * @return
     */
    public static PrivateKey getPrivateKey(String key) {
        try {
            //这个也行
//            byte[] byteKey = new BASE64Decoder().decodeBuffer(key);
            byte[] byteKey = DatatypeConverter.parseBase64Binary(key);

            PKCS8EncodedKeySpec x509EncodedKeySpec = new PKCS8EncodedKeySpec(byteKey);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            return keyFactory.generatePrivate(x509EncodedKeySpec);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 读取文件
     * @param filePath
     * @return
     */
    private static String readFile(String filePath){
        File file = new File(filePath);
        try(Scanner scanner=new Scanner(file)){
            StringBuilder stringBuilder = new StringBuilder();
            while(scanner.hasNext()){
                stringBuilder.append(scanner.next());
            }

            String s = stringBuilder.toString();
            return s.replace("-----BEGINPRIVATEKEY-----","").replace("-----ENDPRIVATEKEY-----","");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取访问微信服务器的httpclient
     * @return
     */
    public static HttpClient getWXHttpClient(){
        String apiV3Key = WXParam.API_V3;
        PrivateKey privateKey = getPrivateKey();
        AutoUpdateCertificatesVerifier verifier = getVerifier();
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(WXParam.MERCHANTID, WXParam.MERCHANT_SERIAL_NUMBER, privateKey)
                .withValidator(new WechatPay2Validator(verifier));

        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签，并进行证书自动更新
        HttpClient httpClient = builder.build();
        return httpClient;
    }

    /**
     * 获取verifier
     * @return
     */
    public static AutoUpdateCertificatesVerifier getVerifier(){
        AutoUpdateCertificatesVerifier verifier = null;
        PrivateKey privateKey = getPrivateKey();
        try {
            verifier = new AutoUpdateCertificatesVerifier(
                    new WechatPay2Credentials(WXParam.MERCHANTID, new PrivateKeySigner(WXParam.MERCHANT_SERIAL_NUMBER, privateKey)),
                    WXParam.API_V3.getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return verifier;
    }

    /**
     * 获取小程序调起支付的签名
     * @param appId
     * @param timeStamp
     * @param nonceStr
     * @param packageStr
     * @return
     */
    public static String getMiniProgramSign(String appId,String timeStamp,String nonceStr,String packageStr){
//        {"prepay_id":"wx111528217145060d54d6345ff317950000"}
        StringBuilder builder = new StringBuilder();
        builder.append(appId);
        builder.append("\n");
        builder.append(timeStamp);
        builder.append("\n");
        builder.append(nonceStr);
        builder.append("\n");
        builder.append(packageStr);
        builder.append("\n");

        String sign = sign(getPrivateKey(), builder.toString());
        return sign;

    }

    /**
    * 签名
     * @param privateKey  私钥
     * @param plainText 明文
     * @return
     */
    public static String sign(PrivateKey privateKey, String plainText) {
            try {
                // 进行签名服务
                Signature signature = Signature.getInstance("SHA256withRSA");
                signature.initSign(privateKey);
                signature.update(plainText.getBytes("UTF-8"));
                byte[] signedData = signature.sign();
                return Base64.getEncoder().encodeToString(signedData);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return "";
    }

    static final int TAG_LENGTH_BIT = 128;
    /**
     * apiv3秘钥
     */
    private static final byte[] aesKey= WXParam.API_V3.getBytes(StandardCharsets.UTF_8);

    /**
     * 解密微信应答报文
     * @param associatedData
     * @param nonce
     * @param ciphertext
     * @return
     * @throws GeneralSecurityException
     * @throws IOException
     */
    public static String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
            throws GeneralSecurityException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

            SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);

            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);

            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }
}
